package com.auqaoasis.model.table.tableFile;

import com.auqaoasis.exception.DbException;
import com.auqaoasis.model.DataBase;
import com.auqaoasis.model.page.heap.HeapPage;
import com.auqaoasis.model.page.heap.HeapPageID;
import com.auqaoasis.model.page.Page;
import com.auqaoasis.model.page.PageID;
import com.auqaoasis.model.row.Row;
import com.auqaoasis.model.table.TableDesc;
import com.auqaoasis.operator.ITableFileIterator;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * @Author Auqaoasis
 * @Date 2022/3/30 - 12:19
 * @Description: 表磁盘文件，存储一个表的数据
 */
public class HeapTableFile implements TableFile {

    /**
     * 文件
     */
    private File file;

    /**
     * 表结构
     */
    private TableDesc tableDesc;

    /**
     * 不移除是不想去改动之前做的测试了
     * @param file
     */
    public HeapTableFile(File file) {
        this.file = file;
    }

    /**
     * 为聚合函数服务，必须通过文件得到结构
     * @param file
     * @param tableDesc
     */
    public HeapTableFile(File file, TableDesc tableDesc) {
        if (tableDesc == null) {
            throw new IllegalArgumentException("tableDesc cant not be null");
        }
        if (file == null) {
            throw new IllegalArgumentException("file cant not be null");
        }
        this.file = file;
        this.tableDesc = tableDesc;
    }

    @Override
    public TableDesc getTableDesc() {
        return tableDesc;
    }

    /**
     * 返回文件的唯一标识
     *
     * @return 表文件的唯一标识
     */
    @Override
    public int getTableId() {
        return this.file.getAbsoluteFile().hashCode();
    }

    /**
     * 按PageID从磁盘中读取一页数据
     *
     * @param pageID
     * @return
     * @throws IOException
     */
    @Override
    public HeapPage readPageFromDisk(PageID pageID) throws IOException {
        byte[] pageData = HeapPage.createEmptyPageData();
        try {
            FileInputStream in = new FileInputStream(file);
            in.skip(pageID.getPageNo() * HeapPage.defaultPageSizeInByte);
            in.read(pageData);
        } catch (IOException e) {
            throw new RuntimeException("todo ,read HeapPage from disk error", e);
        }
        return new HeapPage(pageID, pageData);
    }

    /**
     * 按pageNo从磁盘中写入一页数据
     * 使用RandomAccessFile的写入是 覆盖写入
     *
     * @param page
     */
    @Override
    public void writePageToDisk(Page page) {
        try {
            byte[] pgData = page.serialize();
            RandomAccessFile dbFile = new RandomAccessFile(file, "rws");
            dbFile.skipBytes(page.getPageID().getPageNo() * HeapPage.defaultPageSizeInByte);
            dbFile.write(pgData);
        } catch (IOException e) {
            throw new DbException("write heapPage To disk error", e);
        }
    }

    /**
     * 文件中已存在的page数量
     */
    @Override
    public int getExistPageCount() {
        return (int) file.length() / HeapPage.defaultPageSizeInByte;
    }

    @Override
    public void insertRow(Row row) throws IOException {
        int existPageCount = this.getExistPageCount();
        HeapPage availableHeapPage = (HeapPage) getFirstPageHasEmptySlot(existPageCount);

        // 现有所有页面均没有空slot,新建立一个页面
        if (null == availableHeapPage) {
            // 由于pageNo从0开始
            int pageNo = existPageCount;
            HeapPageID heapPageID = new HeapPageID(this.getTableId(), pageNo);
            availableHeapPage = new HeapPage(heapPageID, HeapPage.createEmptyPageData());
        }
        availableHeapPage.insertRow(row);
        this.writePageToDisk(availableHeapPage);
    }

    @Override
    public void deleteRow(Row row) {

    }
    private Page  getFirstPageHasEmptySlot(int existPageCount) throws IOException {
        if (existPageCount == 0) {
            return null;
        }
        for (int pageNo = 0; pageNo < existPageCount; pageNo++) {
            HeapPageID heapPageID = new HeapPageID(this.getTableId(), pageNo);
            HeapPage pg = this.readPageFromDisk(heapPageID);
            if (pg.hasEmptySlot()) {
                return pg;
            }
        }
        return null;
    }

    @Override
    public ITableFileIterator getIterator() {
        return new HeapTableFileIterator();
    }

    //====================================迭代器======================================

    private class HeapTableFileIterator implements ITableFileIterator {
        private Integer pageNo;
        private int tableId;
        private int existPageCount;
        private Iterator<Row> rowIterator;

        public HeapTableFileIterator() {
            this.pageNo = null;
            this.tableId = getTableId();
            this.existPageCount = getExistPageCount();
            this.rowIterator = null;
        }

        @Override
        public void open() throws DbException {
            pageNo = 0;
            rowIterator = getRowIterator(pageNo);
        }

        @Override
        public boolean hasNext() throws DbException {
            if (pageNo == null) {
                return false; // HeapTableFileIterator 尚未open
            }

            while (pageNo < existPageCount - 1) {
                if (rowIterator.hasNext()) {
                    return true;
                } else {
                    pageNo += 1;
                    rowIterator = getRowIterator(pageNo);
                }
            }
            return rowIterator.hasNext();
        }

        @Override
        public Row next() throws DbException, NoSuchElementException {
            if (!hasNext()) {
                throw new NoSuchElementException("no element ");
            }

            return rowIterator.next();
        }

        @Override
        public void close() {
            pageNo = null;
            rowIterator = null;
        }

        private Iterator<Row> getRowIterator(Integer pageNo) {
            PageID pageID = new HeapPageID(tableId, pageNo);
            return DataBase.getInstance().getPage(pageID).getRowIterator();
        }
    }

}
